本文是基于paddle paddle采用CNN实现猫狗识别案例。
author:小黄
缓慢而坚定的生长
图像分类是根据图像的语义信息将不同类别图像区分开来,是计算机视觉中重要的基本问题
猫狗分类属于图像分类中的粗粒度分类问题
step1.数据准备
import paddle as paddle
import paddle.fluid as fluid
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
import os
(1)数据集介绍
我们使用CIFAR10数据集。CIFAR10数据集包含60,000张32x32的彩色图片,10个类别,每个类包含6,000张。其中50,000张图片作为训练集,10000张作为验证集。这次我们只对其中的猫和狗两类进行预测。
(2)train_reader和test_reader
paddle.dataset.cifar.train10()和test10()分别获取cifar训练集和测试集
paddle.reader.shuffle()表示每次缓存BUF_SIZE个数据项,并进行打乱
paddle.batch()表示每BATCH_SIZE组成一个batch
(3)数据集下载
由于本次实践的数据集稍微比较大,以防出现不好下载的问题,为了提高效率,可以用下面的代码进行数据集的下载。
#!mkdir -p /home/aistudio/.cache/paddle/dataset/cifar/
#!wget “http://ai-atest.bj.bcebos.com/cifar-10-python.tar.gz” -O cifar-10-python.tar.gz
#!mv cifar-10-python.tar.gz /home/aistudio/.cache/paddle/dataset/cifar/
BATCH_SIZE = 128
train_reader = paddle.batch(paddle.reader.shuffle(paddle.dataset.cifar.train10(), buf_size=BATCH_SIZE * 100), batch_size=BATCH_SIZE)
test_reader = paddle.batch(paddle.dataset.cifar.test10(), batch_size=BATCH_SIZE)
Step2.网络配置
(1)网络搭建
在CNN模型中,卷积神经网络能够更好的利用图像的结构信息。下面定义了一个较简单的卷积神经网络。显示了其结构:输入的二维图像,先经过两次卷积层到池化层,再经过全连接层,最后使用softmax分类作为输出层。
池化是非线性下采样的一种形式,主要作用是通过减少网络的参数来减小计算量,并且能够在一定程度上控制过拟合。通常在卷积层的后面会加上一个池化层。paddlepaddle池化默认为最大池化。是用不重叠的矩形框将输入层分成不同的区域,对于每个矩形框的数取最大值作为输出
def convolutional_neural_network(img):conv_pool_1 = fluid.nets.simple_img_conv_pool(input=img, filter_size=5, num_filters=20, pool_size=2, pool_stride=2, act="relu") conv_pool_2 = fluid.nets.simple_img_conv_pool(input=conv_pool_1,filter_size=5,num_filters=50,pool_size=2,pool_stride=2,act="relu")prediction = fluid.layers.fc(input=conv_pool_2, size=10, act='softmax')return prediction
(2)定义数据
data_shape = [3, 32, 32]
images = fluid.layers.data(name='images', shape=data_shape, dtype='float32')
label = fluid.layers.data(name='label', shape=[1], dtype='int64')
(3)获取分类器
predict = convolutional_neural_network(images
(4)定义损失函数和准确率
这次使用的是交叉熵损失函数,该函数在分类任务上比较常用。
定义了一个损失函数之后,还有对它求平均值,因为定义的是一个Batch的损失值。
同时我们还可以定义一个准确率函数,这个可以在我们训练的时候输出分类的准确率。
cost = fluid.layers.cross_entropy(input=predict, label=label)
avg_cost = fluid.layers.mean(cost)
acc = fluid.layers.accuracy(input=predict, label=label)
(5)定义优化方法
这次我们使用的是Adam优化方法,同时指定学习率为0.001
optimizer =fluid.optimizer.Adam(learning_rate=0.001)
optimizer.minimize(avg_cost)
print("完成")
在上述模型配置完毕后,得到两个fluid.Program:fluid.default_startup_program() 与fluid.default_main_program() 配置完毕了。
参数初始化操作会被写入fluid.default_startup_program()
fluid.default_main_program()用于获取默认或全局main program(主程序)。该主程序用于训练和测试模型。fluid.layers 中的所有layer函数可以向 default_main_program 中添加算子和变量。default_main_program 是fluid的许多编程接口(API)的Program参数的缺省值。例如,当用户program没有传入的时候, Executor.run() 会默认执行 default_main_program 。
Step3.模型训练 and Step4.模型评估
(1)创建Executor
首先定义运算场所 fluid.CPUPlace()和 fluid.CUDAPlace(0)分别表示运算场所为CPU和GPU
Executor:接收传入的program,通过run()方法运行program。
place = fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())
(2)定义数据映射器
DataFeeder 负责将reader(读取器)返回的数据转成一种特殊的数据结构,使它们可以输入到 Executor
feeder = fluid.DataFeeder( feed_list=[images, label],place=place)
(3)定义绘制训练过程的损失值和准确率变化趋势的方法draw_train_process
iter=0
iters=[]
train_costs=[]
train_accs=[]
def draw_train_process(iters, train_costs, train_accs):title="training costs/training accs"plt.title(title, fontsize=24)plt.xlabel("iter", fontsize=14)plt.ylabel("cost/acc", fontsize=14)plt.plot(iters, train_costs, color='red', label='training costs')plt.plot(iters, train_accs, color='green', label='training accs')plt.legend()plt.grid()plt.show()
(3)训练并保存模型
Executor接收传入的program,并根据feed map(输入映射表)和fetch_list(结果获取表) 向program中添加feed operators(数据输入算子)和fetch operators(结果获取算子)。 feed map为该program提供输入数据。fetch_list提供program训练结束后用户预期的变量。
每一个Pass训练结束之后,再使用验证集进行验证,并打印出相应的损失值cost和准确率acc。
EPOCH_NUM = 3
model_save_dir = "/home/aistudio/data/catdog.inference.model"for pass_id in range(EPOCH_NUM):train_cost = 0for batch_id, data in enumerate(train_reader()): train_cost,train_acc = exe.run(program=fluid.default_main_program(),feed=feeder.feed(data), fetch_list=[avg_cost, acc]) if batch_id % 100 == 0: print('Pass:%d, Batch:%d, Cost:%0.5f, Accuracy:%0.5f' % (pass_id, batch_id, train_cost[0], train_acc[0]))iter=iter+BATCH_SIZEiters.append(iter)train_costs.append(train_cost[0])train_accs.append(train_acc[0])test_costs = [] test_accs = [] for batch_id, data in enumerate(test_reader()):test_cost, test_acc = exe.run(program=fluid.default_main_program(), feed=feeder.feed(data), fetch_list=[avg_cost, acc]) test_costs.append(test_cost[0]) test_accs.append(test_acc[0]) test_cost = (sum(test_costs) / len(test_costs)) test_acc = (sum(test_accs) / len(test_accs)) print('Test:%d, Cost:%0.5f, ACC:%0.5f' % (pass_id, test_cost, test_acc))if not os.path.exists(model_save_dir):os.makedirs(model_save_dir)fluid.io.save_inference_model(model_save_dir,['images'],[predict],exe)
print('训练模型保存完成!')
draw_train_process(iters, train_costs,train_accs)
Step5.模型预测
(1)创建预测用的Executor
infer_exe = fluid.Executor(place)
inference_scope = fluid.core.Scope()
(2)图片预处理
在预测之前,要对图像进行预处理。
首先将图片大小调整为32*32,接着将图像转换成一维向量,最后再对一维向量进行归一化处理。
def load_image(file):im = Image.open(file)im = im.convert('RGB')im = im.resize((32, 32), Image.ANTIALIAS)im = np.array(im).astype(np.float32)im = im.transpose((2, 0, 1)) im = im / 255.0im = np.expand_dims(im, axis=0)print('im_shape的维度:',im.shape)return im
(3)开始预测
通过fluid.io.load_inference_model,预测器会从params_dirname中读取已经训练好的模型,来对从未遇见过的数据进行预测。
with fluid.scope_guard(inference_scope):[inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model(model_save_dir,infer_exe) infer_path='/home/aistudio/data/dog.png'img = Image.open(infer_path)plt.imshow(img) plt.show() img = load_image(infer_path)results = infer_exe.run(inference_program, feed={feed_target_names[0]: img}, fetch_list=fetch_targets) print('results',results)label_list = ["airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse","ship", "truck"]print("infer results: %s" % label_list[np.argmax(results[0])])